
#include "unistd.h"
#include <linux/power_ic.h>
#include <linux/moto_accy.h>
#include <linux/ezxusbd.h>
#include <linux/apm_bios.h>
#include <linux/fb.h>
#include <linux/pcap_rtc.h>
#include <linux/soundcard.h>
#include <sys/ioctl.h>
#include <fcntl.h>

#include <stdio.h>
#include <time.h>
#include <stdlib.h>


typedef struct 
{
  int time;
  int timeout;
  int direction;
  int bat;
  int cur;
} BAT_INFO;
 
typedef struct
{
  int channel;
  int result;
} CHANNEL_REQUEST;


void bt_on() {
      unsigned short state;
      char *cmdline=malloc(255);



      // power on
      int fd = open("/dev/ttyS1", O_RDWR | O_NOCTTY);
      ioctl(fd, TIOC_BT_VOLTAGE_ON,  &state);
      close(fd);

      // format params
      sprintf(cmdline,"mbtdload /dev/ttyS1 1 921600 %s 1 0 -1",getenv("BT_ADDR"));
      
      // load firmware
      system(cmdline);

}

void bt_off() {
  unsigned short state; 

  int fd = open("/dev/ttyS1", O_RDWR | O_NOCTTY);
  ioctl(fd, TIOC_BT_VOLTAGE_OFF,  &state);
  close(fd); 
}

void usb_mode(int mode) {
      int usb = open("/dev/motusbd", O_RDWR);
      ioctl (usb, MOTUSBD_IOCTL_SET_USBMODE,&mode);
      close(usb);
}

int fb_off() {
  int fb = open("/dev/fb0", O_RDWR);
  int br;


  ioctl(fb, FBIOGETBRIGHTNESS, &br);

  ioctl(fb, FBIOSETBRIGHTNESS, 0);

  ioctl(fb, FBIOSETBKLIGHT, BKLIGHT_OFF);

  ioctl(fb, FBIOBLANK, VESA_POWERDOWN);
  close(fb);

  return br;;
}


void fb_on(int br) {

  int fb = open("/dev/fb0", O_RDWR);
  ioctl(fb, FBIOBLANK, VESA_NO_BLANKING);

  ioctl(fb, FBIOSETBRIGHTNESS, br);

  ioctl(fb, FBIOSETBKLIGHT, BKLIGHT_ON);
  close(fb);
}

void print_usage(const char *argv0)
{
  printf("hwtool - motorola hw control utility\n");
  printf("Usage: %s <command>\n", argv0);
  printf("Available commands:\n");
  printf(" Information:\n");
  printf("  '-t'                     - Print temperature\n");
  printf("  '-b'                     - Print battery level\n");
  printf(" Accessories:\n");
  printf("  '-c'                     - Print charge state\n");
  printf("  '-c <power> <voltage>'   - Set charge state\n");
  printf("  '-C <power> <voltage>'   - Set trickle charge state\n");
  printf("  '-a'                     - Print plugged accessories\n");
  printf("  '-m <mode>'              - Set USB mode\n");
  printf(" Power Management:\n");
  printf("  '-s <core_freq MHz> <core_vltg Mv> <turbo_ratio> <cpu_mode> <fast_bus_mode> <sys_bus_freq MHz> <mem_bus_freq Mhz> <lcd_freq Mhz>'\n");
  printf("                           - Set AP frequencies\n");
  printf("  '-P <mode>'              - Set power path\n");
  printf("  '-S'                     - Suspend AP\n");
  printf(" Bluetooth:\n");
  printf("  '-B'                     - Turn Bluetooth off\n");
  printf("  '-B on'                  - Turn Bluetooth on\n");
  printf(" PCAP_RTC:\n");
  printf("  '-r'                     - Set system time from PCAP_RTC\n");
  printf("  '-r -a'                  - Print PCAP_RTC alarm time\n");
  printf("  '-r -a <timestamp>'      - Set PCAP_RTC alarm time\n");
  printf("  '-r -g'                  - Print PCAP_RTC time\n");
  printf("  '-r -s'                  - Set PCAP_RTC time to system time\n");
  printf("  '-r -s <timestamp>'      - Set PCAP_RTC time\n");
  printf(" Sound Management:\n");
  printf("  '-M <in> <out>           - Set sound mixer routing\n");
  printf("  '-v -i'                  - Print input gain\n");
  printf("  '-v -i <value>'          - Set input gain\n");
  printf("  '-v -o'                  - Print output gain\n");
  printf("  '-v -o <value>'          - Set output gain\n");
  printf("  '-V -i [+|-]<rel_value>' - Relative input gain change\n");
  printf("  '-V -o [+|-]<rel_value>' - Relative output gain change\n");
}

int main(int argc, char *argv[])
{
  int fd;
  int ret;
  BAT_INFO info;
  CHANNEL_REQUEST ch; 
  info.time = 0;

  int n;


  unsigned char charger = 0;
  unsigned char temp = 0;

  

  if (argc < 2 || argv[1][1]=='h') {
    print_usage(argv[0]);
    return 1;
  }

  switch (argv[1][1]){
    // battary
    case 'b':

      fd = open("/dev/power_ic",O_RDWR);
      ret = ioctl (fd,POWER_IC_IOCTL_ATOD_BATT_AND_CURR,&info);
      printf("battary: %d %d\n", info.bat,info.cur);
      break;

    // charger 
    case 'C':
    case 'c':
      fd = open("/dev/power_ic",O_RDWR);

      int currents[] = {0,100,200,300,400,500,600,700,800,900,1000,1100,1200,1300,1800}; // 0-14
      int trickle_currents[] = {0,12,24,36,48,60,72,84}; // 0-7
      int voltages[] = {405,410,415,420,425,430,380,450}; // 0-7

      if (argc == 4) {
        int current = atoi(argv[2]) ;
        int voltage = atoi(argv[3]);

        if (argv[1][1] == 'c') {
          ret = ioctl(fd,POWER_IC_IOCTL_CHARGER_SET_CHARGE_CURRENT,  current);
          printf("set charge current %d mA: %d\n", currents[current],ret);
        } else  {
          ret = ioctl(fd,POWER_IC_IOCTL_CHARGER_SET_TRICKLE_CURRENT, current);
          printf("set trickle charge current %d mA: %d\n", trickle_currents[current],ret);
        }
        ret = ioctl(fd,POWER_IC_IOCTL_CHARGER_SET_CHARGE_VOLTAGE, voltage);
        printf("set charge voltage %1.3f V: %d\n", (float)(voltages[voltage])/100,ret);
    

      } else {
        for (n=0; n<10; n++){
          ch.channel = 3;
          ioctl (fd,POWER_IC_IOCTL_ATOD_SINGLE_CHANNEL, &ch);
          charger += ch.result >> 2;

         }
        printf("charger: %d\n", charger/10);
        break;
      }
      break;

    // temp
    case 't':

      fd = open("/dev/power_ic",O_RDWR);
      ch.channel = 4;
      for (n=0; n<10; n++){
        ioctl (fd,POWER_IC_IOCTL_ATOD_SINGLE_CHANNEL, &ch);
        temp    +=  (ch.result >> 2);


      }
      printf("temp: %d\n", temp/10);
      break;
    case 'a':
      fd = open("/dev/accy",O_RDWR);

      unsigned long int devs;
      int ret;
      char* devinfo[] = 
       {
        "none",
        "invalid",
        "not supported",
        "charger mid",
        "charger mix mpx",
        "charger fast",
        "charger fast mpx",
        "charger fast 3g",
        "carkit mid",
        "carkit fast",
        "carkit smart",
        "usb cable",
        "regression cable",
        "factory cable",
        "mono headset, 2.5mm",
        "stereo headset, 2.5mm",
        "mono headset, miniusb (emu)",
        "stereo headset, miniusb (emu)",
        "stereo headset, no mic, 3.5mm",
        "stereo headset, mic, 3.5mm",
       };

      ioctl(fd, MOTO_ACCY_IOCTL_GET_ALL_DEVICES, &devs);
      while (devs){
        ret = generic_ffs((devs)) - 1;
        printf("%d:%s\n", ret,devinfo[ret]);
        devs &= ~(1 << (ret));


      }

      break;
    case 'm':
      usb_mode ( atoi(argv[2]) );
      break;

    case 's':
      if (argc == 10) {

        fd = open("/dev/apm_bios", O_RDWR);
        struct ipm_config cpu;

        cpu.core_freq     = atoi(argv[2]) * 1000;
        cpu.core_vltg     = atoi(argv[3]);
        cpu.turbo_ratio   = atoi(argv[4]);
        cpu.cpu_mode      = atoi(argv[5]);
        cpu.fast_bus_mode = atoi(argv[6]); 
        cpu.sys_bus_freq  = atoi(argv[7]) * 1000;
        cpu.mem_bus_freq  = atoi(argv[8]) * 1000;
        cpu.lcd_freq      = atoi(argv[9]) * 1000;

        cpu.enabled_device = -1;

        ret = ioctl(fd, APM_IOC_SET_IPM_CONFIG, &cpu);
        printf("apm %d:  %d\n", cpu.core_freq, ret);

        close(fd);
      } else {
        printf(
            "Usage: %s -s <core_freq MHz> <core_vltg Mv> <turbo_ratio> <cpu_mode> <fast_bus_mode> <sys_bus_freq MHz> <mem_bus_freq Mhz> <lcd_freq Mhz>\n",
            argv[0]
        );
      }
      break;
    case 'S':


      n = fb_off();


      fd = open("/dev/apm_bios", O_RDWR);


      usb_mode(0);

      sync();
      int mode =  atoi(argv[2]);
      //ret = ioctl(fd, APM_IOC_SLEEP, mode);
      ret = ioctl(fd, APM_IOC_SUSPEND, mode);
      //printf("APM_IOC_SLEEP %d, %d, %d\n", ret,mode,&mode );
      printf("APM_IOC_SUSPEND %d, %d\n", ret,mode );



      usb_mode(1);

      fb_on(n);

      close(fd);
      break;

    // bluetooth
    case 'B':
      bt_off();


      if (argc == 2) { 

        usleep(200000);
        bt_on();

      }
     break;

    case 'M':

     fd = open("/dev/mixer", O_RDWR);
     int in,out;

     if (argc ==4) {
       in  = atoi(argv[1]);
       out = atoi(argv[2]);

       ioctl(fd, MIXER_WRITE(SOUND_MIXER_RECSRC), &in    );
       ioctl(fd, MIXER_WRITE(SOUND_MIXER_OUTSRC), &out    );
      } else {
       ioctl(fd, MIXER_READ(SOUND_MIXER_RECSRC), &in    );
       ioctl(fd, MIXER_READ(SOUND_MIXER_OUTSRC), &out   );
       printf("mixer in:%d out:%d\n",in,out);
      }

       


     close(fd);

     break;

    case 'P':
     fd = open("/dev/power_ic", O_RDWR);
     ret = ioctl(fd, POWER_IC_IOCTL_CHARGER_SET_POWER_PATH , atoi(argv[2]) );
     printf("POWER_IC_IOCTL_CHARGER_SET_POWER_PATH(%d). ret: %d\n",atoi(argv[2]),ret);
     break;
    
    case 'r':

      fd = open("/dev/pcap_rtc", O_RDWR);

      struct timeval tv = { 0, 0 };
      struct timezone tz;

      // read timezone
      gettimeofday(&tv,&tz);

      printf("sys time\t%s",(char*)ctime(&tv) ) ;

      switch (argc) {
        case 2:
          // read time from pcap
          ret = ioctl(fd, PCAP_RTC_IOC_GET_TIME, &tv);
          printf("pcap time\t%s",(char*)ctime(&tv));

          if (ret) {
            printf("Agggrrhhh... PCAP_RTC_IOC_GET_TIME failed\n");
            return ret;
          } else {
            settimeofday(&tv,&tz); // set system time
            printf("system time set\n");
          }
          break;

        case 3:
          switch (argv[2][1]) {
            case 'a':
              ret = ioctl(fd, PCAP_RTC_IOC_GET_ALARM_TIME, &tv);

              printf("pcap alarm\t%s",(char*)ctime(&tv));
              break;

            case 's':
              ret = ioctl(fd, PCAP_RTC_IOC_SET_TIME, &tv);
              if (!ret)
                printf("pcap time set\t%s",(char*)ctime(&tv));
              else
                printf("failed with error %d\n",ret);

              return ret;

            case 'g':
              ret = ioctl(fd, PCAP_RTC_IOC_GET_TIME, &tv);
              printf("pcap time\t%s",(char*)ctime(&tv));
              break;

          }
          break;
        case 4:
          switch (argv[2][1]) {
            case 'a':
              tv.tv_sec = atoi(argv[3]);

              ret = ioctl(fd, PCAP_RTC_IOC_SET_ALARM_TIME, &tv);

              if (!ret)
                printf("pcap alarm set\t%s",(char*)ctime(&tv));
              else
                printf("failed with error %d\n",ret);

              return ret;

            case 's':
              tv.tv_sec = atoi(argv[3]);

              ret = ioctl(fd, PCAP_RTC_IOC_SET_TIME, &tv);
              if (!ret)
                printf("pcap time set\t%s",(char*)ctime(&tv));
              else
                printf("failed with error %d\n",ret);

              return ret;

          }
          break;


      }
      break;

    case 'v':
     fd = open("/dev/mixer", O_RDWR);
     int vol;

     if (argc==3) {

       if (argv[2][1] == 'i')
        ioctl(fd, MIXER_READ(SOUND_MIXER_IGAIN),&vol);
       else if (argv[2][1] == 'o')
        ioctl(fd, MIXER_READ(SOUND_MIXER_OGAIN),&vol);

       printf("volume: %d\n",vol);

     } else if (argc==4) {

       vol = atoi(argv[3]);

       if (argv[2][1] == 'i')
        ioctl(fd, MIXER_WRITE(SOUND_MIXER_IGAIN),&vol);
       else if (argv[2][1] == 'o')
        ioctl(fd, MIXER_WRITE(SOUND_MIXER_OGAIN),&vol);

     }
     break;

    case 'V':
     fd = open("/dev/mixer", O_RDWR);
     if (argc==4) {

       if (argv[2][1] == 'i')
        ioctl(fd, MIXER_READ(SOUND_MIXER_IGAIN),&vol);
       else if (argv[2][1] == 'o')
        ioctl(fd, MIXER_READ(SOUND_MIXER_OGAIN),&vol);

       if (argv[2][0] == '-')
         vol -= atoi(argv[3]); 
       else if (argv[2][0] == '+')
         vol += atoi(argv[3]); 

       if (argv[2][1] == 'i')
        ioctl(fd, MIXER_WRITE(SOUND_MIXER_IGAIN),&vol);
       else if (argv[2][1] == 'o')
        ioctl(fd, MIXER_WRITE(SOUND_MIXER_OGAIN),&vol);


     } 
     break;
    default:
      print_usage(argv[0]);
      return 1;
    }

  return 0;
}
